package db

import (
	"crypto/sha256"
	"encoding/gob"
	"errors"
	"io"
	"os"
)

type Db struct {
	index     *LazyTreeIndex
	dbF       *os.File
	indexPath string
	offset    int64
}

func (d *Db) DumpIndex() error {

	f, err := os.Create(d.indexPath)
	if err != nil {
		return err
	}

	enc := gob.NewEncoder(f)
	err = enc.Encode(d.index)

	return err
}

func (d *Db) LoadIndex(r io.Reader) error {
	dec := gob.NewDecoder(r)
	err := dec.Decode(d.index)

	return err
}

func (d *Db) Set(key string, val interface{}) error {
	entry, err := NewEntry(key, val)
	if err != nil {
		return err
	}

	buf, err := entry.Dump()
	if err != nil {
		return err
	}
	info := &DbInfo{Key: key, Offset: d.offset}
	d.dbF.Seek(0, io.SeekEnd)
	n, err := d.dbF.Write(buf)
	if err != nil {
		return err
	}
	d.offset += int64(n)

	d.index.Insert(info)

	return nil
}

func (d *Db) Get(key string) (interface{}, error) {
	info := d.index.Find(key)
	if info == nil {
		return nil, errors.New("the key can't exist")
	}

	d.dbF.Seek(info.Offset, io.SeekStart)
	entry := &Entry{}
	err := entry.Load(d.dbF)

	if err != nil {
		return nil, err
	}

	return entry.Parse()
}

func (d *Db) Close() error {
	d.dbF.Close()
	return d.DumpIndex()
}

func NewDb(indexPath, dbPath string) (*Db, error) {
	db := new(Db)
	index := NewLazyTreeIndex(sha256.New())
	db.index = index
	db.indexPath = indexPath

	f, err := os.Open(indexPath)
	if err != nil {
		db.LoadIndex(f)
	}

	fs, err := os.OpenFile(dbPath, os.O_CREATE|os.O_RDWR, 0644)
	if err != nil {
		return nil, err
	}

	offset, err := fs.Seek(0, io.SeekEnd)
	if err != nil {
		return nil, err
	}

	db.offset = offset
	db.dbF = fs

	return db, nil
}
